In [1]:
# This tutorial will demonstrate some implementations of nested control
# structures in Python.   Nested structures very quickly become complicated
# and hard to decipher.  Python requires you to indent your code properly
# which is an advantage because it makes the code easier to read/follow.  
In [2]:
# In the while loop tutorial we should how you could calculate factorials
# using while loops.  Suppose you wanted to restrict users to inputing only
# integer values of n.  You can do this by placing your while loop inside
# an if statement.  The if statement will be used to check for valid
# values of n.
In [5]:
# First, I introduce the % function.  y % x outputs the remainder of
# y/x.
x = 5 % 2
print('5/2 has a remainder of', x)
x = 4 % 2
print('4/2 has a remainder of', x)
5/2 has a remainder of 1
4/2 has a remainder of 0
In [6]:
# So, here is our first nested set of control structures.
n = 4.3
if n % 1 == 0:
    print('n =', n)
    factorial = 1
    while n > 1:
        factorial = factorial*n
        n -= 1
    print('n! =', factorial)
else:
    print('Invalid entry: n is noninteger.')
Invalid entry: n is noninteger.
In [7]:
# Notice that we can still trick the system into producing nonsense by
# entering a negative integer.
n = -4
if n % 1 == 0:
    print('n =', n)
    factorial = 1
    while n > 1:
        factorial = factorial*n
        n -= 1
    print('n! =', factorial)
else:
    print('Invalid entry: n is noninteger.')
n = -4
n! = 1
In [8]:
# To fix this problem, we need to check that n is an integer AND positive.
# We will use 'and' to add another condition to our if statement.  
# We will also implement some elseif statements so that we can present proper 
# error messages.
#
# In the elseif statements != means 'not equal to' and <= means 'less than
# or equal to'.  Try different entries for n.  If you enter, for example, a
# string for n you'll get an error message from Python.  As you can see,
# accounting for all the possible silly inputs users could enter requires a
# lot of thorough and careful programming!
n = 13
if n % 1 == 0 and n > 0:
    print('n =', n)
    factorial = 1
    while n > 1:
        factorial = factorial*n
        n -= 1
    print('n! =', factorial)
elif n == 0:
    print('n =', n)
    print('n! = 1')
elif n % 1 != 0 and n > 0:
    print('Invalid entry: n is noninteger.')
elif n % 1 == 0 and n < 0:
    print('Invalid entry: n negative.')
elif n % 1 != 0 and n < 0:
    print('Invalid entry: n is noninteger and negative.')
n = 13
n! = 6227020800
In [9]:
# Now let's put our factorial code into a for loop so that we can calculate
# n! as a function of n.
import numpy as np
In [10]:
start = 0
finish = 25
ns = np.arange(start, finish + 1, 1)
print(ns)
facts = []
for i in ns:
    if i % 1 == 0 and i > 0:
        n = int(i)
        factorial = 1
        while n > 1:
            factorial = factorial*n
            n -= 1
        facts = facts + [factorial]
    elif i == 0:
        facts = facts + [1]
    elif i % 1 != 0 and n > 0:
        print('Invalid entry: n is noninteger.')
    elif i % 1 == 0 and n < 0:
        print('Invalid entry: n negative.')
    elif i % 1 != 0 and n < 0:
        print('Invalid entry: n is noninteger and negative.')
facts
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25]
Out[10]:
[1,
 1,
 2,
 6,
 24,
 120,
 720,
 5040,
 40320,
 362880,
 3628800,
 39916800,
 479001600,
 6227020800,
 87178291200,
 1307674368000,
 20922789888000,
 355687428096000,
 6402373705728000,
 121645100408832000,
 2432902008176640000,
 51090942171709440000,
 1124000727777607680000,
 25852016738884976640000,
 620448401733239439360000,
 15511210043330985984000000]
In [11]:
# We can now easily make a plot of n! vs n.  Notice how sharply n! rises.
import matplotlib.pyplot as plt
plt.plot(ns, facts, 'bo', fillstyle = 'none')
plt.ylabel('n!')
plt.xlabel('n')
plt.title('linear-linear plot');
In [12]:
# Here's the same data on a log-log scale.
plt.loglog(ns, facts, 'ro', fillstyle = 'none')
plt.ylabel('n!');
plt.xlabel('n')
plt.title('log-log plot');
In [14]:
# Here, we compare n! to exp(n).  Above about n = 5, n! passes exp(n) and
# continues to increase at a higher rate than exp(n).

# First, reproduce the factorial plot...
plt.loglog(ns, facts, 'ro', fillstyle = 'none')
plt.ylabel('n!');
plt.xlabel('n')
plt.title('log-log plot');

# ... the add the exponential data.
xx = np.arange(1, finish, 0.01)
yexp = np.exp(xx)
plt.loglog(xx, yexp, 'k--')
plt.legend(('n!', 'exp(n)'));
In [26]:
# Finally, here's an implementation of our nested control structures in
# which the user is prompted to enter in start and end values for the n!
# calculation and then the plot comparing n! and e^n is generated.
start = int(input("Enter an integer start value: ")) 
finish = int(input("Enter an integer end value: ")) 
ns = np.arange(start, finish + 1, 1)
facts = []
for i in ns:
    if i % 1 == 0 and i > 0:
        n = int(i)
        factorial = 1
        while n > 1:
            factorial = factorial*n
            n -= 1
        facts = facts + [factorial]
    elif i == 0:
        facts = facts + [1]
    elif i % 1 != 0 and n > 0:
        print('Invalid entry: n is noninteger.')
    elif i % 1 == 0 and n < 0:
        print('Invalid entry: n negative.')
    elif i % 1 != 0 and n < 0:
        print('Invalid entry: n is noninteger and negative.')
Enter an integer start value: 1
Enter an integer end value: 120
In [27]:
# Here are plots of n! vs n...
plt.loglog(ns, facts, 'ro', fillstyle = 'none')
plt.ylabel('n!');
plt.xlabel('n')
plt.title('log-log plot')

# and exp(n) vs n
xx = np.arange(start, finish, 0.01)
yexp = np.exp(xx)
plt.loglog(xx, yexp, 'k--')
plt.legend(('n!', 'exp(n)'));